// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走快走。org/ref/mod#go mod tidy了解更多关于go mod tidy的信息。


package modcmd

import (
	"cmd/go/internal/base"
	"cmd/go/internal/cfg"
	"cmd/go/internal/imports"
	"cmd/go/internal/modload"
	"context"
	"fmt"

	"golang.org/x/mod/modfile"
	"golang.org/x/mod/semver"
)

var cmdTidy = &base.Command{
	UsageLine: "go mod tidy [-e] [-v] [-go=version] [-compat=version]",
	Short:     "add missing and remove unused modules",
	Long: `
Tidy makes sure go.mod matches the source code in the module.
It adds any missing modules necessary to build the current module's
packages and dependencies, and it removes unused modules that
don't provide any relevant packages. It also adds any missing entries
to go.sum and removes any unnecessary ones.

The -v flag causes tidy to print information about removed modules
to standard error.

The -e flag causes tidy to attempt to proceed despite errors
encountered while loading packages.

The -go flag causes tidy to update the 'go' directive in the go.mod
file to the given version, which may change which module dependencies
are retained as explicit requirements in the go.mod file.
(Go versions 1.17 and higher retain more requirements in order to
support lazy module loading.)

The -compat flag preserves any additional checksums needed for the
'go' command from the indicated major Go release to successfully load
the module graph, and causes tidy to error out if that version of the
'go' command would load any imported package from a different module
version. By default, tidy acts as if the -compat flag were set to the
version prior to the one indicated by the 'go' directive in the go.mod
file.

	`,
	Run: runTidy,
}

var (
	tidyE      bool          // 如果为真，报告错误，但仍要继续。
	tidyGo     goVersionFlag // go版本，用于向整洁的go写入。mod file（切换延迟加载）
	tidyCompat goVersionFlag // go版本，为其整理go。mod和go。sum文件应该是“兼容的”
)

func init() {
	cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
	cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
	cmdTidy.Flag.Var(&tidyGo, "go", "")
	cmdTidy.Flag.Var(&tidyCompat, "compat", "")
	base.AddModCommonFlags(&cmdTidy.Flag)
}

// goVersionFlag是一个标志。值，表示受支持的Go版本。
// 
// （请注意，'go mod edit'的-go参数*不是*一个goVersionFlag。
// 它故意允许使用比支持的版本更新的参数。）
type goVersionFlag struct {
	v string
}

func (f *goVersionFlag) String() string { return f.v }
func (f *goVersionFlag) Get() any       { return f.v }

func (f *goVersionFlag) Set(s string) error {
	if s != "" {
		latest := modload.LatestGoVersion()
		if !modfile.GoVersionRE.MatchString(s) {
			return fmt.Errorf("expecting a Go version like %q", latest)
		}
		if semver.Compare("v"+s, "v"+latest) > 0 {
			return fmt.Errorf("maximum supported Go version is %s", latest)
		}
	}

	f.v = s
	return nil
}

func runTidy(ctx context.Context, cmd *base.Command, args []string) {
	if len(args) > 0 {
		base.Fatalf("go: 'go mod tidy' accepts no arguments")
	}

	// Tidy的目标是使“go test”对“all”中的任何包都是可复制的，因此我们
	// 需要包含测试依赖项。对于之前指定go 1.15或
	// 的模块，这是不可操作的（因为'all'会饱和可传递测试
	// 依赖项）。
	// 
	// 但是，对于延迟加载（go 1.16+）“all”只包括主模块通过传递方式导入的
	// 包，而不包括
	// 这些包的测试依赖项。为了使除主模块之外的“全部”中的包
	// 的“go test”可复制，我们必须明确
	// 要求包含其测试依赖项。
	modload.ForceUseModules = true
	modload.RootMode = modload.NeedRoot

	modload.LoadPackages(ctx, modload.PackageOpts{
		GoVersion:                tidyGo.String(),
		Tags:                     imports.AnyTags(),
		Tidy:                     true,
		TidyCompatibleVersion:    tidyCompat.String(),
		VendorModulesInGOROOTSrc: true,
		ResolveMissingImports:    true,
		LoadTests:                true,
		AllowErrors:              tidyE,
		SilenceMissingStdImports: true,
	}, "all")
}
